/**
 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
 * This file is part of CSipSimple.
 *
 *  CSipSimple is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  If you own a pjsip commercial license you can also redistribute it
 *  and/or modify it under the terms of the GNU Lesser General Public License
 *  as an android library.
 *
 *  CSipSimple is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with CSipSimple.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.wireless.serialservice;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.security.InvalidParameterException;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.os.PowerManager;


public class SerialService extends Service {
	private static final String THIS_FILE = "SerialService";
	private SerialServiceExecutor mExecutor;
	private SerWakeLock sWakeLock;
	private static HandlerThread executorThread;
	private static SerialService the_service_instance_ = null;
	public SerialPortFinder mSerialPortFinder = new SerialPortFinder();
	private SerialPort mSerialPort = null;
	protected OutputStream mOutputStream;
	private InputStream mInputStream;
	private ReadThread mReadThread;
	private SerialCommand mSerialCommand;
	
	public static boolean isready() {
         return (the_service_instance_ != null);
    }

    public static SerialService instance() {
         if (the_service_instance_ == null) {
                 throw new RuntimeException(
                                 "the_service_instance_ not instanciated yet");
         } else {
                 return the_service_instance_;
         }
     }
    
    public static void startService(Context ctx)
    {
          Intent intent = new Intent(ctx, SerialService.class);
          ctx.startService(intent);
    }

 
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
	 @Override
	 public void onCreate() {
	    super.onCreate();
	    the_service_instance_ = this;
	    
	    sWakeLock = new SerWakeLock((PowerManager) getSystemService(Context.POWER_SERVICE));
	    
	    try {
			mSerialPort = initSerialPort();
			mOutputStream = mSerialPort.getOutputStream();
			mInputStream = mSerialPort.getInputStream();

			/* Create a receiving thread */
			mReadThread = new ReadThread();
			mReadThread.start();
		} catch (InvalidParameterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	 }
	
	 @Override
     public void onDestroy() {
             super.onDestroy();
             the_service_instance_ = null;
     }

	 public SerialPort initSerialPort() throws SecurityException, IOException, InvalidParameterException {
			if (mSerialPort == null) {
				/* Read serial port parameters */
				String path = "/dev/ttyS3";
				int baudrate = 2400;

				/* Check parameters */
				if ( (path.length() == 0) || (baudrate == -1)) {
					throw new InvalidParameterException();
				}
				/* Open the serial port */
				mSerialPort = new SerialPort(new File(path), baudrate, 0);
			}
			return mSerialPort;
		}
	 
	public SerialPort getSerialPort() {
		return this.mSerialPort;
	}
	
	private class ReadThread extends Thread {
		@Override
		public void run() {
			super.run();
			while(!isInterrupted()) {
				int size;
				try {
					byte[] buffer = new byte[64];
					if (mInputStream == null) return;
					size = mInputStream.read(buffer);
					if (size > 0) {
						System.out.println("===========ReadThread=========");
						printHexString(buffer, size);
						//bytesToHexString(buffer, size);
						onDataReceived(buffer, size);
					}
				} catch (IOException e) {
					e.printStackTrace();
					return;
				}
			}
		}

		private void onDataReceived(byte[] buffer, int size) {
			// TODO Auto-generated method stub
			
		}
	}
	
	public static void printHexString(byte[] b, int size) { 
		//System.out.print(hint); 
		for (int i = 0; i < size; i++) { 
		String hex = Integer.toHexString(b[i] & 0xFF); 
		if (hex.length() == 1) { 
		hex = '0' + hex; 
		} 
		System.out.print(hex.toUpperCase() + " "); 
		} 
		System.out.println(""); 
     } 
	
	public SerialServiceExecutor getExecutor() {
        if (mExecutor == null) {
        	mExecutor = new SerialServiceExecutor(this);
        }
        return mExecutor;
    }
	
	public static class SerialServiceExecutor extends Handler {
        WeakReference<SerialService> handlerService;
        
        SerialServiceExecutor(SerialService s) {
            super(createLooper());
            handlerService = new WeakReference<SerialService>(s);
        }

        public void execute(Runnable task) {
        	SerialService s = handlerService.get();
            if(s != null) {
                s.sWakeLock.acquire(task);
            }
            Message.obtain(this, 0/* don't care */, task).sendToTarget();
        }

        @Override
        public void handleMessage(Message msg) {
	        if (msg.obj instanceof Runnable) {
                executeInternal((Runnable) msg.obj);
            } else {
                Log.w(THIS_FILE, "can't handle msg: " + msg);
            }
        }

        private void executeInternal(Runnable task) {
            try {
                task.run();
            } catch (Throwable t) {
                Log.e(THIS_FILE, "run task: " + task, t);
            } finally {

            	SerialService s = handlerService.get();
                if(s != null) {
                    s.sWakeLock.release(task);
                }
            }
        }
    }
	
	private static Looper createLooper() {
	    //	synchronized (executorThread) {
		    	if(executorThread == null) {
		    		Log.d(THIS_FILE, "Creating new handler thread");
		    		// ADT gives a fake warning due to bad parse rule.
			        executorThread = new HandlerThread("SerialService.Executor");
			        executorThread.start();
		    	}
		//	}
	        return executorThread.getLooper();
	}
	
	public void sendOpenCameraCommand() {
		mSerialCommand =  new SerialCommand();
		try {
			if (mOutputStream != null) {
					mOutputStream.write(mSerialCommand.buildOpenCommand2());
				mOutputStream.flush();
				
			} else {
				return;
			}
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}	
	}
	
	public void sendDialCommand(String number) {
		mSerialCommand =  new SerialCommand();
		//while (!isInterrupted()) {
			try {
				if (mOutputStream != null) {

					mOutputStream.write(DialCommand());

					mOutputStream.flush();
					try {
						Thread.sleep(300);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					mOutputStream.write(mSerialCommand.buildCommand(number));//mSerialCommand.buildCommand(number));

					mOutputStream.flush();
				} else {
					return;
				}
			} catch (IOException e) {
				e.printStackTrace();
				return;
			}
		
	}
	
	public void sendSerCameraCommand() {
		mSerialCommand =  new SerialCommand();
			try {
				if (mOutputStream != null) {
						mOutputStream.write(mSerialCommand.buildSerCameraCommand());
					   mOutputStream.flush();
					
				} else {
					return;
				}
			} catch (IOException e) {
				e.printStackTrace();
				return;
			}
		
	}
	
	public void sendUnlockCommand() {
		mSerialCommand =  new SerialCommand();
			try {
				if (mOutputStream != null) {
						mOutputStream.write(mSerialCommand.buildOpenCommand2());
					mOutputStream.flush();
					try {
						Thread.sleep(300);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					if (mOutputStream != null) {
						mOutputStream.write(mSerialCommand.buildOpenCommand());
					  mOutputStream.flush();
					}
				} else {
					return;
				}
			} catch (IOException e) {
				e.printStackTrace();
				return;
			}
		
	}
	
	
	public abstract static class SerRunnable  implements Runnable {
		protected abstract void doRun() throws SameThreadException;
		
		public void run() {
			try {
				doRun();
			}catch(SameThreadException e) {
				Log.e(THIS_FILE, "Not done from same thread");
			}
		}
	}
	
	public class SameThreadException extends Exception {
		private static final long serialVersionUID = -905639124232613768L;

		public SameThreadException() {
			super("Should be launched from a single worker thread");
		}
	}
	
	private byte[] DialCommand() {
		byte[] mBuffer = new byte[19];
		mBuffer[0] = (byte) 0xFA;
		mBuffer[1] = (byte) 0xFC;
		mBuffer[2] = (byte) 0xEB;
		mBuffer[3] = (byte) 0x00;
		mBuffer[4] = (byte) 0x12;

		mBuffer[5] = (byte) 0xFE;
		mBuffer[6] = (byte) 0xFE;
		mBuffer[7] = (byte) 0xFF;
		mBuffer[8] = (byte) 0xFF;
		mBuffer[9] = (byte) 0x00;
		mBuffer[10] = (byte) 0x00;
		mBuffer[11] = (byte) 0x02;
		mBuffer[12] = (byte) 0x03;
		mBuffer[13] = (byte) 0x00;
		mBuffer[14] = (byte) 0x00;
		mBuffer[15] = (byte) 0x01;
		mBuffer[16] = (byte) 0x01;
		mBuffer[17] = (byte) 0x01;
		mBuffer[18] = (byte) 0x01;

		return mBuffer;
	}
	//FA FE E5 00 13 00 00 00 00 00 00 00 00 00 00 00 02 01 05
	private byte[] DialCommand2() {
		byte[] mBuffer = new byte[19];
		mBuffer[0] = (byte) 0xFA;
		mBuffer[1] = (byte) 0xFE;
		mBuffer[2] = (byte) 0xE5;
		mBuffer[3] = (byte) 0x00;
		mBuffer[4] = (byte) 0x13;

		mBuffer[5] = (byte) 0x00;
		mBuffer[6] = (byte) 0x00;
		mBuffer[7] = (byte) 0x00;
		mBuffer[8] = (byte) 0x00;
		mBuffer[9] = (byte) 0x00;
		mBuffer[10] = (byte) 0x00;
		mBuffer[11] = (byte) 0x00;
		mBuffer[12] = (byte) 0x00;
		mBuffer[13] = (byte) 0x00;
		mBuffer[14] = (byte) 0x00;
		mBuffer[15] = (byte) 0x00;
		mBuffer[16] = (byte) 0x02;
		mBuffer[17] = (byte) 0x01;
		mBuffer[18] = (byte) 0x05;

		/*mBuffer[7] = (byte) (~(mBuffer[0] + mBuffer[1] + mBuffer[2]
				+ mBuffer[3] + mBuffer[4] + mBuffer[5] + mBuffer[6]));*/

		return mBuffer;
	}
	
	//add by xuweihong
	public void SerialCall(String number) {
		 final String peer_caller = number;
		getExecutor().execute(new SerRunnable() {
            @Override
            protected void doRun() throws SameThreadException {
            	SerialService.instance().sendDialCommand(peer_caller);
           }
        });
	}
	
	
	public void SerialSerCameraCall() {
		 getExecutor().execute(new SerRunnable() {
          @Override
          protected void doRun() throws SameThreadException {
          	SerialService.instance().sendSerCameraCommand();
         }
      });
	}
	
	public void SerialHandup()  {
		getExecutor().execute(new SerRunnable() {
            @Override
            protected void doRun() throws SameThreadException {
            	SerialService.instance().sendCloseCommand();
           }
        });
	}
	
	protected void sendCloseCommand() {
		// TODO Auto-generated method stub
		mSerialCommand =  new SerialCommand();
			try {
				if (mOutputStream != null) {
					 mOutputStream.write(mSerialCommand.buildCloseCommand());
					   mOutputStream.flush();
					
				} else {
					return;
				}
			} catch (IOException e) {
				e.printStackTrace();
				return;
			}	
	
	}

	public void SerialUnLock() {
		getExecutor().execute(new SerRunnable() {
            @Override
            protected void doRun() throws SameThreadException {
            	SerialService.instance().sendUnlockCommand();
           }
        });
	}
}
